#include <memory>
#include <type_traits>

#include "backend/device/txda_device.h"

#include "operators/handle.h"
#include "backend/kernel_interface/kernel_scheduler.h"
#include "backend/kernel_cache.h"

namespace txdnn {
/*
 *  HandleImpl Definition and Implementation
 */
struct HandleImpl {
  using StreamPtr = std::shared_ptr<typename std::remove_pointer<txdaStream_t>::type>;

  HandleImpl() {}

  void setCtx() const { } // TODO set_device(); 


  bool enable_profiling_            = false;
  StreamPtr stream_                 = nullptr;
  float profiling_result_           = 0.0f;

  KernelCache cache_;

  std::string device_name_;
  std::size_t num_tiles_            = 0;
  std::size_t local_mem_size_       = 0;
  std::size_t global_mem_size_      = 0;
  std::size_t max_mem_alloc_size_   = 0;
};


/*
 *  Handle Implementation
 */
Handle::Handle() : impl_(std::make_unique<HandleImpl>()) {

}

Handle::~Handle() {}

Invoker Handle::prepareInvoker(const InvokerFactory& factory,
                               const std::vector<operators::KernelInfo>& kernels,
                               std::vector<backend::Program>* programs_out) const {
  
  std::vector<backend::Kernel> built;
  built.reserve(kernels.size());

  if (programs_out != nullptr) {
    programs_out->resize(kernels.size());
  }

  for (auto i = 0; i < kernels.size(); ++i) {
    const auto& k = kernels[i];
    backend::Program* program_out = programs_out != nullptr? &(*programs_out)[i] : nullptr;
    const auto kernel = impl_->cache_.addKernel(*this, "", "", k.kernel_path, k.kernel_name, k.l_wk, k.g_wk, k.compile_options, kernels.size(), "", program_out);
    built.emplace_back(kernel);
  }
  
  return factory(built);
}


txdnn::backend::KernelInvoke Handle::run(txdnn::backend::Kernel k, bool multi_graph) const {
  impl_->setCtx();
  auto callback = nullptr; // TODO to add event to 
  return k.invoke();  // TODO add params
}


backend::Program Handle::loadProgram(const fs::path& program_path,
                                     std::string params,
                                     const std::string& kernel_src,
                                     bool force_attach_binary) const {

  impl_->setCtx();
  // TODO
  auto p = backend::ProgramMainCGroup{};

  return p;
}


} // namespace txdnn